package com.siebre.picclife.asynctask;

import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import javax.sql.DataSource;

import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.FluentIterable;
import com.siebre.orm.hibernate.AuditInterceptor;
import com.siebre.picclife.asynctask.model.impl.TaskEntity;
import com.siebre.repository.GeneralRepository;
import com.siebre.repository.rdb.hibernate.HibernateGeneralRepository;
import com.siebre.security.UserImpl;

@Configuration
@EnableTransactionManagement
public class PersistenceConfig {
	
	private Logger logger = LoggerFactory.getLogger(PersistenceConfig.class);
	
	@Bean
	GeneralRepository generalRepository() {
		return new HibernateGeneralRepository();
	}
	
	@Bean
	HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
		return new HibernateTransactionManager(sessionFactory);
	}
	
	@SuppressWarnings("rawtypes")
	@Bean
	SessionFactory sessionFactory(List<EntityTypeProvider> providers) throws Exception {
		AnnotationSessionFactoryBean factory = new AnnotationSessionFactoryBean();
		
		factory.setDataSource(dataSource());
		
		factory.setAnnotatedClasses(FluentIterable.from(providers)
				.transformAndConcat(new Function<EntityTypeProvider, Iterable<? extends Class>>() {

					public Iterable<? extends Class> apply(EntityTypeProvider provider) {
						return provider.provide();
					}
				}).toArray(Class.class));
		
		Properties properties = new Properties();
		properties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
		factory.setHibernateProperties(properties);
		
		factory.setEntityInterceptor(new AuditInterceptor());
		
		factory.afterPropertiesSet();
		return factory.getObject();
	}
	
	@Bean
	EntityTypeProvider defaultEntityTypeProvider() {
		return new EntityTypeProvider() {

			public Iterable<Class<?>> provide() {
				return Arrays.<Class<?>>asList(TaskEntity.class, UserImpl.class);
			}
		};
	}
	
	@Autowired(required = false)
	private DatabaseNameProvider databaseName;
	
	@Bean
	DataSource dataSource() {
		String name = Optional.fromNullable(databaseName)
			.transform(Suppliers.<String>supplierFunction())
			.or("task-repo-default");
		logger.debug("database name: {}", name);
		return new EmbeddedDatabaseBuilder().setName(name).build();
	}
	
	/**
	 * 
	 * @author ZhangChi
	 * @since 2015年4月8日
	 *
	 */
	public static interface EntityTypeProvider {
		
		Iterable<Class<?>> provide();
	}
	
	/**
	 * 
	 * @author ZhangChi
	 * @since 2015年4月19日
	 *
	 */
	public static class DatabaseNameProvider implements Supplier<String> {
		
		private final Supplier<String> delegate;

		public DatabaseNameProvider(Supplier<String> delegate) {
			this.delegate = delegate;
		}

		public String get() {
			return delegate.get();
		}
	}
}